﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using AtomicTypes;
using QLabValues;

namespace NumericalCalculations
{
    public partial class Functions
    {
        public List<IQLabValue> figure(List<IQLabValue> o)
        {

            if (o.Count > 1)
                throw (new NumericalCalculationsException("??? Error using ==> figure \n Too many input arguments."));

            if (o.Count == 0)
            {
                //Create new figure with the first available index
                int firstAvailableIndex = Plotter.Instance.FirstAvailableIndex();
                Plotter.Instance.SelectFigure(firstAvailableIndex);
                return new List<IQLabValue> { new QFloat64NDimArray(new[] { new QFloat64(firstAvailableIndex) }, new[] { 1, 1 }) };
            }

            if (o.Count == 1)
            {
                int index;

                if (o.ElementAt(0).GetEnumType() == IQLabValueType.QFloat64NDimArray)
                {
                    var of = o.Select(oel => new QFloat64NDimArray(oel)).ToList();
                    index = (int) of.ElementAt(0).Data[0].Real;
                }
                else
                {
                    throw (new NumericalCalculationsException("??? Undefined function or method 'figure' for input arguments of type " +
                        Helper.PrintEnumName(o.ElementAt(0).GetEnumType()) + "."));
                }
                
                Plotter.Instance.SelectFigure(index);
                return new List<IQLabValue> { new QFloat64NDimArray(new[] { new QFloat64(index) }, new[] { 1, 1 }) };
            }

            return new IQLabValue[] { new QCharNDimArray("") }.ToList();
        }

        public List<IQLabValue> hold(List<IQLabValue> o)
        {
            bool holdValue = false;
            string holdStatus = string.Empty;
            var on = new QCharNDimArray(new[] {new QChar('o'), new QChar('n')}, new[] {1, 2});
            var off = new QCharNDimArray(new[] {new QChar('o'), new QChar('f'), new QChar('f')}, new[] {1, 3});

            switch (o.Count)
            {
                case 0:
                    //No arguments toggles the value
                    holdValue = !Plotter.Instance.CurrentFigure.Hold;
                    holdStatus = holdValue ? "Current plot held" : "Current hold released";
                    break;
                case 1:
                    IQLabValue qLabValue = o.ElementAt(0);
                    if (qLabValue.GetEnumType() == IQLabValueType.QCharNDimArray)
                    {
                        if (Helper.areIQlabValueEqual(qLabValue, on))
                        {
                            holdValue = true;
                        }
                        else if (Helper.areIQlabValueEqual(qLabValue, off))
                        {
                            holdValue = false;
                        }
                        else
                        {
                            throw (new NumericalCalculationsException("??? Error using ==> hold \n Unknown command option."));                            
                        }
                    }
                    break;
                default:
                    throw (new NumericalCalculationsException("??? Error using ==> hold \n Too many input arguments."));
                    break;
            }


            Plotter.Instance.CurrentFigure.Hold = holdValue;

            throw (new NumericalCalculationsException(holdStatus));
        }

        public List<IQLabValue> clf(List<IQLabValue> o)
        {
            if (o.Count > 2)
            {
                throw (new NumericalCalculationsException("??? Error using ==> clf \n Too many input arguments."));
            }

            if (o.Count == 0)
            {
                Plotter.Instance.CurrentFigure.Clear(false);
                throw (new NumericalCalculationsException(string.Empty));
            }

            var reset = new QCharNDimArray(new[]
                                               {
                                                   new QChar('r'),
                                                   new QChar('e'),
                                                   new QChar('s'),
                                                   new QChar('e'),
                                                   new QChar('t'),
                                               }, new[] { 1, 5 });
            Figure figure = null;
            bool resetValue = false;

            if (o.Count >= 1)
            {
                //MATLAB doesn't care about order of arguments in clf function
                foreach (var qLabValue in o)
                {
                    var argType = qLabValue.GetEnumType();
                    if (argType == IQLabValueType.QFloat64NDimArray)
                    {
                        var qLabFloat = new QFloat64NDimArray(qLabValue);
                        int index = (int)qLabFloat.Data[0].Real;

                        figure = Plotter.Instance.GetFigure(index);
                        if (figure == null)
                        {
                            throw (new NumericalCalculationsException("??? Error using ==> clf \n Invalid figure handle"));
                        }

                    }
                    else if (argType == IQLabValueType.QCharNDimArray)
                    {
                        if (Helper.areIQlabValueEqual(qLabValue, reset))
                        {
                            resetValue = true;
                        }
                        else
                        {
                            throw (new NumericalCalculationsException("??? Error using ==> clf \n Unknown command option."));
                        }
                    }
                    else
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'clf' for input arguments of type " +
                            Helper.PrintEnumName(argType) + "."));
                    }
                }

                if (figure == null)
                    figure = Plotter.Instance.CurrentFigure;
                
                figure.Clear(resetValue);                
            }

            return new IQLabValue[] { new QCharNDimArray("") }.ToList();
        }

        public List<IQLabValue> axis(List<IQLabValue> o)
        {
            if (o.Count == 0)
                return Plotter.Instance.CurrentFigure.GetAxis();

            foreach (var argument in o)
            {
                var argType = argument.GetEnumType();
                if (argType == IQLabValueType.QCharNDimArray)
                {
                    var chars = new QCharNDimArray(argument);
                    if (Helper.IsString(chars))
                    {
                        var option = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                        switch (option) //We don't use ToLower() because MATLAB acts this way
                        {
                            case "on":
                                Plotter.Instance.CurrentFigure.AxisVisibility = true;
                                break;
                            case "off":
                                Plotter.Instance.CurrentFigure.AxisVisibility = false;
                                break;
                            case "auto":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Auto);
                                break;
                            case "manual":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Manual);
                                break;
                            case "tight":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Tight);
                                break;
                            case "fill":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Fill);
                                break;
                            case "ij":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.IJ);
                                break;
                            case "xy":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.XY);
                                break;
                            case "equal":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Equal);
                                break;
                            case "image":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Image);
                                break;
                            case "square":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Square);
                                break;
                            case "normal":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Normal);
                                break;
                            case "vis3d":
                                Plotter.Instance.CurrentFigure.SetAxisRange(Plotter.AxisRangeMode.Vis3d);
                                break;
                            default:
                                throw (new NumericalCalculationsException(String.Format("Unknown command option {0}", option)));
                        }

                    }
                }
                else if (argType == IQLabValueType.QFloat64NDimArray)
                {
                    var vector = new QFloat64NDimArray(argument);
                    if (!Helper.IsVector(vector))
                        throw (new NumericalCalculationsException("Argument must be vector."));
                    switch (vector.Data.Count())
                    {
                        case 4:
                            Plotter.Instance.CurrentFigure.SetAxisRange(
                                vector.Data[0].Real,
                                vector.Data[1].Real,
                                vector.Data[2].Real,
                                vector.Data[3].Real);
                            break;
                        case 6:
                            Plotter.Instance.CurrentFigure.SetAxisRange(
                                vector.Data[0].Real,
                                vector.Data[1].Real,
                                vector.Data[2].Real,
                                vector.Data[3].Real,
                                vector.Data[4].Real,
                                vector.Data[5].Real);
                            break;
                        case 8:
                            Plotter.Instance.CurrentFigure.SetAxisRange(
                                vector.Data[0].Real,
                                vector.Data[1].Real,
                                vector.Data[2].Real,
                                vector.Data[3].Real,
                                vector.Data[4].Real,
                                vector.Data[5].Real,
                                vector.Data[6].Real,
                                vector.Data[7].Real);
                            break;
                        default:
                            throw (new NumericalCalculationsException("Vector must have 4, 6, or 8 elements."));
                    }
                }
            }
            return new IQLabValue[] { new QCharNDimArray("") }.ToList();
        }

        public List<IQLabValue> legend(List<IQLabValue> o)
        {
            var labels = new List<string>();
            bool first = true;
            foreach (var argument in o)
            {
                var argType = argument.GetEnumType();
                if (argType == IQLabValueType.QCharNDimArray)
                {
                    var chars = new QCharNDimArray(argument);
                    if (Helper.IsString(chars))
                        labels.Add(Helper.QCharNDimArrayToStringList(chars).ElementAt(0));
                }

                if (first)
                {
                    first = false;
                    if (labels.Count == 1)
                    {
                        switch (labels[0].ToLower())
                        {
                            case "toggle":
                                Plotter.Instance.CurrentFigure.LegendVisible = !Plotter.Instance.CurrentFigure.LegendVisible;
                                throw (new NumericalCalculationsException(""));
                            case "hide":
                            case "off":
                                Plotter.Instance.CurrentFigure.LegendVisible = false;
                                throw (new NumericalCalculationsException(""));
                            case "show":
                                Plotter.Instance.CurrentFigure.LegendVisible = true;
                                throw (new NumericalCalculationsException(""));
                            case "boxon":
                                Plotter.Instance.CurrentFigure.LegendBoxVisible = true;
                                throw (new NumericalCalculationsException(""));
                            case "boxoff":
                                Plotter.Instance.CurrentFigure.LegendBoxVisible = false;
                                throw (new NumericalCalculationsException(""));

                        }
                    }
                }
            }

            return new IQLabValue[] { new QCharNDimArray(Plotter.Instance.CurrentFigure.SetLegend(labels)) }.ToList();
        }

        public List<IQLabValue> grid(List<IQLabValue> o)
        {
            if (o.Count > 1)
            {
                throw (new NumericalCalculationsException("??? Error using ==> grid \n Too much input arguments."));
            }

            if (o.Count == 0)
            {
                Plotter.Instance.CurrentFigure.ToggleGrid();
            }

            if (o.Count == 1)
            {
                var argType = o.ElementAt(0).GetEnumType();
                if (argType == IQLabValueType.QCharNDimArray)
                {
                    var chars = new QCharNDimArray(o.ElementAt(0));
                    if (Helper.IsString(chars))
                    {
                        var option = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                        switch (option.ToLower()) //We use ToLower() because MATLAB acts this way
                        {
                            case "on":
                                Plotter.Instance.CurrentFigure.SetGrid(Plotter.GridMode.Major);
                                break;
                            case "off":
                                Plotter.Instance.CurrentFigure.SetGrid(Plotter.GridMode.None);
                                break;
                            case "minor":
                                Plotter.Instance.CurrentFigure.SetGrid(Plotter.GridMode.Minor);
                                break;

                            default:
                                throw (new NumericalCalculationsException(String.Format("Unknown command option {0}", option)));
                        }

                    }
                }

            }

            return new IQLabValue[] { new QCharNDimArray(string.Empty) }.ToList();
        }

        public List<IQLabValue> xlabel(List<IQLabValue> o)
        {
            if (o.Count == 0)
            {
                throw (new NumericalCalculationsException("??? Error using ==> xlabel \n Not enough input arguments."));
            }

            if (o.Count > 1)
            {
                //TODO: Support more arguments
                throw (new NumericalCalculationsException("??? Error using ==> xlabel \n More than one argument is not supported yet."));
            }

            if (o.Count == 1)
            {
                var argument = o.ElementAt(0);
                var argType = argument.GetEnumType();

                if (argType == IQLabValueType.QCharNDimArray)
                {
                    var chars = new QCharNDimArray(argument);
                    if (!Helper.IsString(chars))
                    {
                        throw (new NumericalCalculationsException("??? Error using ==> xlabel \n Argument must be string."));
                    }

                    var argumentString = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                    Plotter.Instance.CurrentFigure.SetXLabel(argumentString);
                }
                else
                {
                    throw (new NumericalCalculationsException("??? Undefined function or method 'xlabel' for input arguments of type " +
                            Helper.PrintEnumName(argType) + "."));
                }
            }

            return new IQLabValue[] { new QCharNDimArray(string.Empty) }.ToList();
        }

        public List<IQLabValue> ylabel(List<IQLabValue> o)
        {
            if (o.Count == 0)
            {
                throw (new NumericalCalculationsException("??? Error using ==> ylabel \n Not enough input arguments."));
            }

            if (o.Count > 1)
            {
                //TODO: Support more arguments
                throw (new NumericalCalculationsException("??? Error using ==> ylabel \n More than one argument is not supported yet."));
            }

            if (o.Count == 1)
            {
                var argument = o.ElementAt(0);
                var argType = argument.GetEnumType();

                if (argType == IQLabValueType.QCharNDimArray)
                {
                    var chars = new QCharNDimArray(argument);
                    if (!Helper.IsString(chars))
                    {
                        throw (new NumericalCalculationsException("??? Error using ==> ylabel \n Argument must be string."));
                    }

                    var argumentString = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                    Plotter.Instance.CurrentFigure.SetYLabel(argumentString);
                }
                else
                {
                    throw (new NumericalCalculationsException("??? Undefined function or method 'ylabel' for input arguments of type " +
                            Helper.PrintEnumName(argType) + "."));
                }
            }

            return new IQLabValue[] { new QCharNDimArray(string.Empty) }.ToList();
        }

        public List<IQLabValue> title(List<IQLabValue> o)
        {
            
            if (o.Count == 0)
            {
                throw (new NumericalCalculationsException("??? Error using ==> title \n Not enough input arguments."));
            }

            if (o.Count > 1)
            {
                //TODO: Support more arguments
                throw (new NumericalCalculationsException("??? Error using ==> title \n More than one argument is not supported yet."));
            }

            if (o.Count == 1)
            {
                var argument = o.ElementAt(0);
                var argType = argument.GetEnumType();

                if (argType == IQLabValueType.QCharNDimArray)
                {
                    var chars = new QCharNDimArray(argument);
                    if (!Helper.IsString(chars))
                    {
                        throw (new NumericalCalculationsException("??? Error using ==> title \n Argument must be string."));
                    }

                    var argumentString = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                    Plotter.Instance.CurrentFigure.SetTitle(argumentString);
                }
                else
                {
                    throw (new NumericalCalculationsException("??? Undefined function or method 'title' for input arguments of type " +
                            Helper.PrintEnumName(argType) + "."));
                }
            }

            return new IQLabValue[] { new QCharNDimArray(string.Empty) }.ToList();
        }

        public List<IQLabValue> plot(List<IQLabValue> o)
        {
            if (o.Count == 0)
            {
                throw (new NumericalCalculationsException("??? Error using ==> plot \n Not enough input arguments."));
            }
            var toPlotList = new List<Tuple<double[], double[], string>>();

            if (o.Count == 1 || (o.Count == 2 && o.ElementAt(1).GetEnumType() == IQLabValueType.QCharNDimArray))
            {

                if(o.ElementAt(0).GetEnumType() != IQLabValueType.QFloat64NDimArray)
                    throw (new NumericalCalculationsException("??? Error using ==> plot \n Invalid first data argument"));

                var epsilon = Math.Pow(1, -9);

                var qLabFloat = new QFloat64NDimArray(o.ElementAt(0));
                int count = qLabFloat.Data.Count();
                var reals = new double[count];
                var imags = new double[count];
                var ordinals = new double[count];
                bool isComplex = false;
                for (int i = 0; i < count; i++)
                {
                    reals[i] = qLabFloat.Data[i].Real;
                    imags[i] = qLabFloat.Data[i].Imag;
                    ordinals[i] = i + 1;

                    if (Math.Abs(imags[i]) > epsilon)
                        isComplex = true;
                }
                string options = "";
                if (o.Count == 2)
                {
                    var chars = new QCharNDimArray(o.ElementAt(1));
                    if (!Helper.IsString(chars))
                        throw (new NumericalCalculationsException("??? Error using ==> plot \n Argument must be string."));

                    options = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                }

                if (isComplex)
                    toPlotList.Add(Tuple.Create(reals, imags, options));
                else
                    toPlotList.Add(Tuple.Create(ordinals, reals, options));
            }
            else
            {
                int i = 0;
                int lastIndex = o.Count - 1;
                while (i <= lastIndex)
                {
                    if (i > lastIndex || o.ElementAt(i).GetEnumType() != IQLabValueType.QFloat64NDimArray)
                        throw (new NumericalCalculationsException("??? Error using ==> plot \n Data must be a single matrix Y or a list of pairs X,Y"));

                    if (i + 1 > lastIndex || o.ElementAt(i + 1).GetEnumType() != IQLabValueType.QFloat64NDimArray)
                        throw (new NumericalCalculationsException("??? Error using ==> plot \n Data must be a single matrix Y or a list of pairs X,Y"));


                    var firstVector = new QFloat64NDimArray(o.ElementAt(i));
                    var secondVector = new QFloat64NDimArray(o.ElementAt(i + 1));

                    int count = firstVector.Data.Count();
                    if (secondVector.Data.Count() != count)
                        throw (new NumericalCalculationsException("??? Error using ==> plot \n Vectors must be the same lengths."));

                    var reals1 = new double[count];
                    var reals2 = new double[count];
                    for (int j = 0; j < count; j++)
                    {
                        reals1[j] = firstVector.Data[j].Real;
                        reals2[j] = secondVector.Data[j].Real;
                    }

                    string options = "";
                    if (i + 2 <= lastIndex && o.ElementAt(i + 2).GetEnumType() == IQLabValueType.QCharNDimArray)
                    {
                        var chars = new QCharNDimArray(o.ElementAt(i + 2));
                        if (!Helper.IsString(chars))
                            throw (new NumericalCalculationsException("??? Error using ==> plot \n Argument must be string."));

                        options = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                        i += 3;
                    }
                    else
                    {
                        i += 2;
                    }

                    toPlotList.Add(Tuple.Create(reals1, reals2, options));
                }
            }

            var parsedPlots = new List<Tuple<double[],double[], Color?, Plotter.PointTypes, Plotter.LineTypes>>();

            foreach (var tuple in toPlotList)
            {
                Color? c = null;
                var pt = Plotter.PointTypes.None;
                bool pointTypeSet = false;
                var lt = Plotter.LineTypes.None;
                bool lineTypeSet = false;
                string options = tuple.Item3;
                for (int i = 0; i < tuple.Item3.Length; i++)
                {
                    const string optionsParsingErrorString = "??? Error using ==> plot \n Error in color/linetype argument";
                    switch (options[i])
                    {
                        case 'b':
                            if (c.HasValue)throw (new NumericalCalculationsException(optionsParsingErrorString));
                            c = Color.Blue;
                            break;
                        case 'g':
                            if (c.HasValue)throw (new NumericalCalculationsException(optionsParsingErrorString));
                            c = Color.Green;
                            break;
                        case 'r':
                            if (c.HasValue)throw (new NumericalCalculationsException(optionsParsingErrorString));
                            c = Color.Red;
                            break;
                        case 'm':
                            if (c.HasValue)throw (new NumericalCalculationsException(optionsParsingErrorString));
                            c = Color.Magenta;
                            break;
                        case 'y':
                            if (c.HasValue)throw (new NumericalCalculationsException(optionsParsingErrorString));
                            c = Color.Yellow;
                            break;
                        case 'k':
                            if (c.HasValue)throw (new NumericalCalculationsException(optionsParsingErrorString));
                            c = Color.Black;
                            break;
                        case 'w':
                            if (c.HasValue)throw (new NumericalCalculationsException(optionsParsingErrorString));
                            c = Color.White;
                            break;
                        case '-':
                            if (i == options.Length - 1)
                            {
                                if (lineTypeSet)
                                    throw (new NumericalCalculationsException(optionsParsingErrorString));
                                lt = Plotter.LineTypes.Solid;
                                lineTypeSet = true;
                            }
                            else
                            {
                                if (options[i + 1] == '.')
                                {
                                    if (lineTypeSet)
                                        throw (new NumericalCalculationsException(optionsParsingErrorString));
                                    lt = Plotter.LineTypes.DashDot;
                                    lineTypeSet = true;
                                    i++;
                                }
                                else if (options[i + 1] == '-')
                                {
                                    if (lineTypeSet)
                                        throw (new NumericalCalculationsException(optionsParsingErrorString));
                                    lt = Plotter.LineTypes.Dashed;
                                    lineTypeSet = true;
                                    i++;
                                }
                                else
                                {
                                    if (lineTypeSet)
                                        throw (new NumericalCalculationsException(optionsParsingErrorString));
                                    lt = Plotter.LineTypes.Solid;
                                    lineTypeSet = true;
                                }
                            }
                            break;
                        case ':':
                            if (lineTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            lt = Plotter.LineTypes.Dotted;
                            lineTypeSet = true;
                            break;
                        case '.':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Point;
                            pointTypeSet = true;
                            break;
                        case 'o':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Circle;
                            pointTypeSet = true;
                            break;
                        case 'x':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.XMark;
                            pointTypeSet = true;
                            break;
                        case '+':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Plus;
                            pointTypeSet = true;
                            break;
                        case '*':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Star;
                            pointTypeSet = true;
                            break;
                        case 's':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Square;
                            pointTypeSet = true;
                            break;
                        case 'd':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Diamond;
                            pointTypeSet = true;
                            break;
                        case 'v':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.TriangleDown;
                            pointTypeSet = true;
                            break;
                        case '^':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.TriangleUp;
                            pointTypeSet = true;
                            break;
                        case '<':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.TriangleLeft;
                            pointTypeSet = true;
                            break;
                        case '>':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.TriangleRight;
                            pointTypeSet = true;
                            break;
                        case 'p':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Pentagram;
                            pointTypeSet = true;
                            break;
                        case 'h':
                            if (pointTypeSet)
                                throw (new NumericalCalculationsException(optionsParsingErrorString));
                            pt = Plotter.PointTypes.Hexagram;
                            pointTypeSet = true;
                            break;
                    }
                }
                parsedPlots.Add(Tuple.Create(tuple.Item1, tuple.Item2, c, pt, lt));
            }

            //At this point there were no parsing errors
            int colorIndex = -1;
            bool oldHold = Plotter.Instance.CurrentFigure.Hold;
            for (int i = 0; i < parsedPlots.Count; i++)
            {
                if (i > 0)
                {
                    Plotter.Instance.CurrentFigure.Hold = true;
                }

                Plotter.Instance.CurrentFigure.Plot(
                    parsedPlots[i].Item1,
                    parsedPlots[i].Item2,
                    parsedPlots[i].Item3,
                    parsedPlots[i].Item4,
                    parsedPlots[i].Item5,
                    parsedPlots[i].Item3.HasValue? 0 : ++colorIndex);
            }
            Plotter.Instance.CurrentFigure.Hold = oldHold;
            Plotter.Instance.CurrentFigure.RefreshGraph();


            return new IQLabValue[] { new QCharNDimArray(string.Empty) }.ToList();
        }

        public List<IQLabValue> bar(List<IQLabValue> o)
        {
            if (o.Count == 0)
            {
                throw (new NumericalCalculationsException("??? Error using ==> bar \n Not enough input arguments."));
            }


            QFloat64NDimArray y = null;
            List<double> xValues = null;
            double? width = null;
            Color? c = null;
            for (int i = 0; i < o.Count; i++)
            {
                if (i == 0)
                {
                    if (o.ElementAt(0).GetEnumType() != IQLabValueType.QFloat64NDimArray)
                        throw (new NumericalCalculationsException(
                            "??? Error using ==> bar \n Must supply Y data or X and Y data as first argument(s)."));
                    y = new QFloat64NDimArray(o.ElementAt(0));
                    continue;
                }



                if (o.ElementAt(i).GetEnumType() == IQLabValueType.QFloat64NDimArray)
                {
                    var next = new QFloat64NDimArray(o.ElementAt(i));
                    if (xValues == null)
                    {
                        if (width != null)
                            throw (new NumericalCalculationsException(
                                "??? Error using ==> bar \n Too many input arguments."));
                        
                        if (CheckSizeForBarFunctionAndResolveXValues(y,next,out xValues))
                        {
                            y = next;
                            continue;
                        }

                        if (next.Data.Count() == 1)
                        {
                            width = next.Data[0].Real;
                            continue;
                        }

                        throw (new NumericalCalculationsException("??? Error using ==> bar \n The length of X must match the number of rows of Y."));
                    }
                    
                    if (width == null)
                    {
                        if (next.Data.Count() == 1)
                        {
                            width = next.Data[0].Real;
                            continue;
                        }                        
                    }

                    throw (new NumericalCalculationsException("??? Error using ==> bar \n Too many input arguments."));                        
                }
                
                if (o.ElementAt(i).GetEnumType() == IQLabValueType.QCharNDimArray)
                {
                    if(c != null)
                        throw (new NumericalCalculationsException("??? Error using ==> bar \n Too many input arguments."));                        
                        
                    var chars = new QCharNDimArray(o.ElementAt(i));
                    if (!Helper.IsString(chars))
                    {
                        throw (new NumericalCalculationsException("??? Error using ==> bar \n Incorect color argument."));
                    }

                    var colorString = Helper.QCharNDimArrayToStringList(chars).ElementAt(0);
                    if (colorString.Length != 1)
                        throw (new NumericalCalculationsException("??? Error using ==> bar \n Incorect color argument."));

                    switch (colorString.ToLower()[0])
                    {

                        case 'b':
                            c = Color.Blue;
                            break;
                        case 'g':
                            c = Color.Green;
                            break;
                        case 'r':
                            c = Color.Red;
                            break;
                        case 'm':
                            c = Color.Magenta;
                            break;
                        case 'y':
                            c = Color.Yellow;
                            break;
                        case 'k':
                            c = Color.Black;
                            break;
                        case 'w':
                            c = Color.White;
                            break;
                        default:
                            throw (new NumericalCalculationsException("??? Error using ==> bar \n Incorect color argument."));
                    }
                }
                else
                {
                    throw (new NumericalCalculationsException("??? Error using ==> bar \n Incorect input."));
                }
                
            }

            Plotter.Instance.CurrentFigure.Bar(xValues, y, c, width);
            Plotter.Instance.CurrentFigure.RefreshGraph();

            return new IQLabValue[] { new QCharNDimArray(string.Empty) }.ToList();
        }

        private static bool CheckSizeForBarFunctionAndResolveXValues(QFloat64NDimArray xdata, QFloat64NDimArray ydata, out List<double> xvalues)
        {
            if (xdata == null || ydata == null)
            {
                xvalues = null;
                return false;
            }

            if (Helper.IsVector(xdata))
            {
                if (xdata.Data.Count() == ydata.DimensionSize[0])
                {
                    xvalues = new List<double>();
                    for (int i = 0; i < xdata.Data.Count(); i++)
                    {
                        if (xvalues.Contains(xdata.Data[i].Real))
                            throw (new NumericalCalculationsException(
                                "??? Error using ==> bar \n XData cannot contain duplicate values."));
                        xvalues.Add(xdata.Data[i].Real);
                    }
                    return true;
                }
            }
            else if (xdata.Dimension == 2)
            {
                // if xdata is matix, then dimensions must be the same...
                if (Helper.checkDimensions(xdata, ydata))
                {
                    //...and if they are, we take values from the last column of xdata (as does MATLAB in such case)
                    xvalues = new List<double>();
                    for (int i = 0; i < xdata.DimensionSize[0]; i++)
                    {
                        var lastColumnOfThatRow = xdata.DimensionSize[1]*(i + 1) - 1;
                        if (xvalues.Contains(xdata.Data.ElementAt(lastColumnOfThatRow).Real))
                            throw (new NumericalCalculationsException(
                                "??? Error using ==> bar \n XData cannot contain duplicate values."));
                        xvalues.Add(xdata.Data[lastColumnOfThatRow].Real);
                    }
                    return true;
                }
            }
            else
                throw (new NumericalCalculationsException(
                    "??? Error using ==> bar \n XData cannot have more then 2 dimensions."));

            xvalues = null;
            return false;
        }
    }
}
